package Controle;

import util.Constantes;
import java.awt.Color;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

import util.MultiMap;

import Jplay.GameImage;
import Jplay.TileInfo;
import Jplay.Time;
import MotorGrafico.Indice;
import MotorGrafico.Tabuleiro;

/**
 * Classe respons�vel por armazenar o estado do jogo.
 * 
 * @author Gefersom C. Lima
 * 
 */
public class JogoEstado {

	private boolean habilitouRelogio;
	private int jogadorDaVez;
	private MultiMap pecasBrancasRemovidas;
	private MultiMap pecasPretasRemovidas;
	private Tabuleiro tabuleiro;
	private Time tempoJogador1;
	private Time tempoJogador2;
	private int somaJogadas;
	private Time tempoTotal;

	/**
	 * Construtor da classe.
	 * 
	 * @param tabuleiro
	 *            Inst�ncia do tabuleiro.
	 */
	public JogoEstado(Tabuleiro tabuleiro) {
		this.tabuleiro = tabuleiro;
		this.tempoJogador1 = new Time(0, 0, true);
		this.tempoJogador2 = new Time(0, 0, true);
		this.pecasBrancasRemovidas = new MultiMap();
		this.pecasPretasRemovidas = new MultiMap();
		this.somaJogadas = somaJogadas;
        this.tempoTotal = new Time(0, 0, true);
	}

	/**
	 * Readiciona um tipo de pe�a ao tabuleiro.
	 * 
	 * @param tipoPeca
	 *            Id representante da pe�a a ser adicionada.
	 * @param quantidade
	 *            Quantidade de pe�as a serem adicionadas.
	 */
	private void restaurarEstadoPecasRemovidas(int tipoPeca, int quantidade) {
		for (int i = 0; i < quantidade; i++) {
			addPecaListaPecasRemovidas(tipoPeca);
		}
	}

	/**
	 * Adiciona a lista de pe�as comidas a pe�a que estiver na posi��o passada
	 * como par�metro.
	 * 
	 * @param ind
	 *            Posi��o (i,j) da pe�a no tabuleiro.
	 */
	public void pecaComida(Indice ind) {
		TileInfo t = this.tabuleiro.getTile(ind.i, ind.j);
		if (t.id != Constantes.ESTADO_VAZIO) {
			int tipoPeca = tabuleiro.getPeca(ind.i, ind.j);
			addPecaListaPecasRemovidas(tipoPeca);
		}
	}
	
	public Boolean comeu(Indice ind) {
		TileInfo t = this.tabuleiro.getTile(ind.i, ind.j);
		if (this.pecasPretasRemovidas.equals(true) || 
								this.pecasBrancasRemovidas.equals(true)){
			return true;
		}
		return false;
	}

	/**
	 * Adiciona a lista de pe�as removidas uma pe�a.
	 * 
	 * @param tipoPeca
	 *            Tipo de pe�a que deve ser adicionada a lista de pe�as
	 *            removidas.											
	 */
	public void addPecaListaPecasRemovidas(int tipoPeca) {
		switch (tipoPeca) {
		case Constantes.WHITE_BISPO:
			this.pecasBrancasRemovidas.add(Constantes.WHITE_BISPO,
					new GameImage(Constantes.IMG_MINIATURA_WHITE_BISPO));
			break;
		case Constantes.WHITE_CAVALO:
			this.pecasBrancasRemovidas.add(Constantes.WHITE_CAVALO,
					new GameImage(Constantes.IMG_MINIATURA_WHITE_CAVALO));
			break;
		case Constantes.WHITE_PEAO:
			this.pecasBrancasRemovidas.add(Constantes.WHITE_PEAO,
					new GameImage(Constantes.IMG_MINIATURA_WHITE_PEAO));
			break;
		case Constantes.WHITE_RAINHA:
			this.pecasBrancasRemovidas.add(Constantes.WHITE_RAINHA,
					new GameImage(Constantes.IMG_MINIATURA_WHITE_RAINHA));
			break;
		case Constantes.WHITE_REI:
			this.pecasBrancasRemovidas.add(Constantes.WHITE_REI, new GameImage(
					Constantes.IMG_MINIATURA_WHITE_REI));
			break;
		case Constantes.WHITE_TORRE:
			this.pecasBrancasRemovidas.add(Constantes.WHITE_TORRE,
					new GameImage(Constantes.IMG_MINIATURA_WHITE_TORRE));
			break;
		case Constantes.BLACK_BISPO:
			this.pecasPretasRemovidas.add(Constantes.BLACK_BISPO,
					new GameImage(Constantes.IMG_MINIATURA_BLACK_BISPO));
			break;
		case Constantes.BLACK_CAVALO:
			this.pecasPretasRemovidas.add(Constantes.BLACK_CAVALO,
					new GameImage(Constantes.IMG_MINIATURA_BLACK_CAVALO));
			break;
		case Constantes.BLACK_PEAO:
			this.pecasPretasRemovidas.add(Constantes.BLACK_PEAO, new GameImage(
					Constantes.IMG_MINIATURA_BLACK_PEAO));
			break;
		case Constantes.BLACK_RAINHA:
			this.pecasPretasRemovidas.add(Constantes.BLACK_RAINHA,
					new GameImage(Constantes.IMG_MINIATURA_BLACK_RAINHA));
			break;
		case Constantes.BLACK_REI:
			this.pecasPretasRemovidas.add(Constantes.BLACK_REI, new GameImage(
					Constantes.IMG_MINIATURA_BLACK_REI));
			break;
		case Constantes.BLACK_TORRE:
			this.pecasPretasRemovidas.add(Constantes.BLACK_TORRE,
					new GameImage(Constantes.IMG_MINIATURA_BLACK_TORRE));
			break;
		default:
			System.out
					.println("Tem coisa errada na remocao de pe�a branca do tabuleiro.");
			break;
		}
	}

	/**
	 * Ler um estado que estar� armazenado em um arquivo. O par�metro passado
	 * serve para identificar o estado que deve ser carregado.
	 * 
	 * @param idEstado
	 *            - Id do estado a ser restaurado.
	 * @return OK, caso n�o haja erro, NOK caso haja algum erro.
	 * @see Constantes.
	 */
	public int carregarEstado(int idEstado) {
		tabuleiro.loadFromFile(Constantes.ARQ_ESTADOS_TABULEIRO + idEstado);

		BufferedReader in = null;

		try {
			in = new BufferedReader(new FileReader(new File(
					Constantes.ARQ_ESTADOS_TIMER_PONTUACAO + idEstado)));

			String line = in.readLine();
			if (Integer.parseInt(line) == Constantes.PECA_BRANCA) {
				tempoJogador1.setColor(Color.green);
				tempoJogador2.setColor(Color.gray);
				jogadorDaVez = Constantes.JOGADOR_01;
			} else {
				tempoJogador2.setColor(Color.green);
				tempoJogador1.setColor(Color.gray);
				jogadorDaVez = Constantes.JOGADOR_02;
			}

			habilitouRelogio = true;

			pecasBrancasRemovidas.clear();
			pecasPretasRemovidas.clear();

			line = in.readLine();
			String[] campos = line.split(";");

			tempoJogador2.setTime(campos[0]);
			restaurarEstadoPecasRemovidas(Constantes.WHITE_BISPO,
					Integer.parseInt(campos[1]));
			restaurarEstadoPecasRemovidas(Constantes.WHITE_CAVALO,
					Integer.parseInt(campos[2]));
			restaurarEstadoPecasRemovidas(Constantes.WHITE_PEAO,
					Integer.parseInt(campos[3]));
			restaurarEstadoPecasRemovidas(Constantes.WHITE_RAINHA,
					Integer.parseInt(campos[4]));
			restaurarEstadoPecasRemovidas(Constantes.WHITE_REI,
					Integer.parseInt(campos[5]));
			restaurarEstadoPecasRemovidas(Constantes.WHITE_TORRE,
					Integer.parseInt(campos[6]));

			line = in.readLine();
			campos = line.split(";");
			tempoJogador1.setTime(campos[0]);
			restaurarEstadoPecasRemovidas(Constantes.WHITE_BISPO,
					Integer.parseInt(campos[1]));
			restaurarEstadoPecasRemovidas(Constantes.WHITE_CAVALO,
					Integer.parseInt(campos[2]));
			restaurarEstadoPecasRemovidas(Constantes.WHITE_PEAO,
					Integer.parseInt(campos[3]));
			restaurarEstadoPecasRemovidas(Constantes.WHITE_RAINHA,
					Integer.parseInt(campos[4]));
			restaurarEstadoPecasRemovidas(Constantes.WHITE_REI,
					Integer.parseInt(campos[5]));
			restaurarEstadoPecasRemovidas(Constantes.WHITE_TORRE,
					Integer.parseInt(campos[6]));
		} catch (IOException e) {
			return Constantes.NOK;
		}
		return Constantes.OK;
	}

	/**
	 * Pausa o tempo do jogador que tiver a vez de jogar.
	 */
	public void pausarTempo() {
		if (jogadorDaVez == Constantes.JOGADOR_01)
			tempoJogador2.stop();
		else
			tempoJogador1.stop();
	}

	/**
	 * Retorna a contagem do tempo do jogador que est� na vez de jogar.
	 */
	public void continuarContagemTempo() {
		if (jogadorDaVez == Constantes.JOGADOR_02)
			tempoJogador2.start();
		else
			tempoJogador1.start();
	}

	/**
	 * Retorna o n�mero de pe�as existente no Multimap que sejam de um
	 * determinado tipo.
	 * 
	 * @param pecas
	 *            - Multimap contendo pe�as.
	 * @param idPeca
	 *            - Tipo de pe�a a ter a sua quantidade avaliada.
	 * @return int
	 */
	private int calcPecas(MultiMap pecas, int idPeca) {
		List temp = (List) pecas.get(idPeca);
		return (temp == null ? 0 : temp.size());
	}

	/**
	 * Salva o estado corrente do jogo em um arquivo identificado pelo par�metro
	 * passado.
	 * 
	 * @param idEstado
	 *            - Id do estado que deve ser salvo.
	 */
	public void salvarEstado(int idEstado) {
		tabuleiro.saveToFile(Constantes.ARQ_ESTADOS_TABULEIRO + idEstado);

		BufferedWriter out = null;

		try {
			out = new BufferedWriter(new FileWriter(new File(
					Constantes.ARQ_ESTADOS_TIMER_PONTUACAO + idEstado)));

			if (jogadorDaVez == Constantes.JOGADOR_02)
				out.append(Constantes.PECA_NEGRA + "\n");
			else
				out.append(Constantes.PECA_BRANCA + "\n");

			out.append(tempoJogador2.toString() + ";");
			out.append(calcPecas(pecasBrancasRemovidas, Constantes.BLACK_BISPO)
					+ ";");
			out.append(calcPecas(pecasBrancasRemovidas, Constantes.BLACK_CAVALO)
					+ ";");
			out.append(calcPecas(pecasBrancasRemovidas, Constantes.BLACK_PEAO)
					+ ";");
			out.append(calcPecas(pecasBrancasRemovidas, Constantes.BLACK_RAINHA)
					+ ";");
			out.append(calcPecas(pecasBrancasRemovidas, Constantes.BLACK_REI)
					+ ";");
			out.append(calcPecas(pecasBrancasRemovidas, Constantes.BLACK_TORRE)
					+ ";\n");

			out.append(tempoJogador1.toString() + ";");
			out.append(calcPecas(pecasPretasRemovidas, Constantes.WHITE_BISPO)
					+ ";");
			out.append(calcPecas(pecasPretasRemovidas, Constantes.WHITE_CAVALO)
					+ ";");
			out.append(calcPecas(pecasPretasRemovidas, Constantes.WHITE_PEAO)
					+ ";");
			out.append(calcPecas(pecasPretasRemovidas, Constantes.WHITE_RAINHA)
					+ ";");
			out.append(calcPecas(pecasPretasRemovidas, Constantes.WHITE_REI)
					+ ";");
			out.append(calcPecas(pecasPretasRemovidas, Constantes.WHITE_TORRE)
					+ ";\n");

			out.close();

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Retorna as pe�as brancas removidas.
	 * 
	 * @return MultiMap
	 */
	public MultiMap getPecasBrancasRemovidas() {
		return pecasBrancasRemovidas;
	}

	/**
	 * Retorna as pe�as pretas removidas.
	 * 
	 * @return MultiMap
	 */
	public MultiMap getPecasPretasRemovidas() {
		return pecasPretasRemovidas;
	}

	/**
	 * Retorna se o rel�gio foi habilitado ou n�o.
	 * 
	 * @return true, caso sim, false, caso n�o.
	 */
	public boolean isHabilitouRelogio() {
		return habilitouRelogio;
	}

	/**
	 * Seta o valor booleano para a ativa��o do rel�gio. True significa que o
	 * rel�gio foi habilitado, false, significa que o rel�gio foi desabilitado.
	 * 
	 * @param habilitouRelogio
	 */
	public void setHabilitouRelogio(boolean habilitouRelogio) {
		this.habilitouRelogio = habilitouRelogio;
	}

	/**
	 * Retorna um inteiro que identifica o jogador que deve realizar a jogada.
	 * 
	 * @return int
	 * @see Constantes
	 */
	public int getJogadorDaVez() {
		return jogadorDaVez;
	}

	/**
	 * Seta qual � o jogador da vez a realizar a jogada.
	 * 
	 * @param jogadorDaVez
	 *            - Identificador inteiro do jogador que deve realizar a jogada.
	 * @see Constantes.
	 */
	public void setJogadorDaVez(int jogadorDaVez) {
		this.jogadorDaVez = jogadorDaVez;
	}

	/**
	 * Retorna o tempo corrente do jogador 1.
	 * 
	 * @return Time
	 */

	public Time getTempoJogador1() {
		return tempoJogador1;
	}

	/**
	 * Retorna o tempo corrente do jogador 2.
	 * 
	 * @return Time
	 */
	public Time getTempoJogador2() {
		return tempoJogador2;
	}
}
